/*
 *only support for "exact division"
 *
 *
 * */



#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "2d5p_aceMesh.h"
#include "task.h"

#define alpha_d 0.0876
#define beta_d  0.0765

__thread_local volatile unsigned long get_reply,put_reply;


//core region 
__thread_local double A_lhs[BLKX][BLKY];
__thread_local double A_rhs[BLKX+2][BLKY];
//left and right shade  region
__thread_local double A_ls[BLKX];
__thread_local double A_rs[BLKX];
//Record the logical information of the last time executed task.
__thread_local int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
__thread_local int last_iter,last_starti,last_startj;


#ifdef _PROFILING
__thread_local volatile unsigned long start,end;
__thread_local volatile unsigned long dma_start1,dma_end1,dma_start2,dma_end2,dma_start3,dma_end3,dma_start4,dma_end4,dma_start5,dma_end5;
__thread_local volatile unsigned long comp_start,comp_end;
__thread_local int core_id=0;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
extern unsigned long slave_count[64];
extern int reuse_num[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

extern double A[2][SIZEX][SIZEY];

void stencil_core(targs* args)
{
   int bsize,stride,tsize; 
   dma_start1=dma_end1=dma_start2=dma_end2=dma_start3=dma_end3=dma_start4=dma_end4=0;
   s_iter = args->iter;
   dest = (s_iter) % 2 ;
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
   start=rtc_();
   core_id=athread_get_id(-1);
   if((s_iter-1) == last_iter && s_starti == last_starti && s_startj == last_startj)
   {
   reuse_num[core_id]++;
#ifdef _PROFILING
   dma_start1=rtc_();
#endif
     get_reply = 0;
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj],&A_rhs[0][0],BLKY*8,(void*)&get_reply,0,0,0);
     athread_get(PE_MODE,&A[src][s_endi][s_startj], &A_rhs[BLKX+1][0],BLKY*8,(void*)&get_reply,0,0,0);
     while(get_reply != 2);
     //1.2 update left and right shade data 
     get_reply = 0;
     stride = (SIZEY-1)*8;
     tsize = BLKX*8;
     bsize = 8;
     athread_get(PE_MODE,&A[src][s_starti][s_startj-1]\
                   , &A_ls[0]\
                   , tsize,(void*)&get_reply\
                   , 0, stride, bsize);
     athread_get(PE_MODE,&A[src][s_starti][s_endj]
                   , &A_rs[0]
                   , tsize,(void*)&get_reply
                   , 0, stride, bsize); 
     while(get_reply != 2);
 //    for(i=s_starti;i<s_endi;++i)
    // {
      //  A_ls[i-s_starti]=A[src][i][s_startj-1];
      //  A_rs[i-s_starti]=A[src][i][s_endj];
   //  }  
#ifdef _PROFILING
   dma_end1=rtc_();
   slave_dma_count[core_id]+=((dma_end1-dma_start1)); 
#endif
     //compute core data
     for(i = 1; i < BLKX-1; i++)
       for(j = 1; j < BLKY-1; j++)
          A_rhs[i+1][j] = alpha_d * (A_lhs[i][j]) + \
                     beta_d * (A_lhs[i-1][j] + A_lhs[i+1][j] +\
                     A_lhs[i][j-1] + A_lhs[i][j+1]);
     A_rhs[1][0] = alpha_d * (A_lhs[0][0]) + \
                   beta_d * (A_rhs[0][0] + A_lhs[1][0] +\
                   A_ls[0] + A_lhs[0][1]);
     A_rhs[BLKX][0] =   alpha_d * (A_lhs[BLKX-1][0]) +\
                     beta_d * (A_lhs[BLKX-2][0] + A_rhs[BLKX+1][0] +\
                     A_ls[BLKX-1] + A_lhs[BLKX-1][1]);
     A_rhs[1][BLKY-1] = alpha_d * (A_lhs[0][BLKY-1]) + \
                     beta_d * (A_rhs[0][BLKY-1] +\
                     A_lhs[1][BLKY-1] +\
                     A_lhs[0][BLKX-2] + A_rs[0]);
     A_rhs[BLKX][BLKY-1] = alpha_d * \
                     (A_lhs[BLKX-1][BLKY-1]) + \
                     beta_d * (A_lhs[BLKX-2][BLKY-1] + \
                     A_rhs[BLKX+1][BLKY-1] + \
                     A_lhs[BLKX-1][BLKY-2] + \
                     A_rs[BLKX-1]);
     for(j = 1;j < BLKY-1; ++j)
     {
       A_rhs[1][j] = alpha_d * (A_lhs[0][j]) + \
                     beta_d * (A_rhs[0][j] + A_lhs[1][j] +\
                     A_lhs[0][j-1] + A_lhs[0][j+1]);
       A_rhs[BLKX][j] = alpha_d * (A_lhs[BLKX-1][j]) + \
                    beta_d * (A_lhs[BLKX-2][j] + A_rhs[BLKX+1][j] +\
                     A_lhs[BLKX-1][j-1] + A_lhs[BLKX-1][j+1]);
     }
     for(i = 1;i < BLKX-1; ++i)
     {
        A_rhs[i+1][0] = alpha_d * (A_lhs[i][0]) + \
                     beta_d * (A_lhs[i-1][0] + A_lhs[i+1][0] +\
                     A_ls[i] + A_lhs[i][1]);
        A_rhs[i+1][BLKY-1] = alpha_d * (A_lhs[i][BLKY-1]) + \
                     beta_d * (A_lhs[i-1][BLKY-1] + A_lhs[i+1][BLKY-1] +\
                     A_lhs[i][BLKY-2] + A_rs[i]);

     }
#ifdef _PROFILING
   dma_start2=rtc_();
#endif
     //write back
     put_reply=0;
     bsize=(BLKY)*8;
     stride=(SIZEY-(BLKY))*8;  //stride: from blocktail to blockhead
     tsize= bsize*(BLKX);
     athread_put(PE_MODE,&A_rhs[1][0]
                , &A[dest][s_starti][s_startj]
                ,tsize,(void*)&put_reply,stride,bsize);
     while(put_reply!=1);
#ifdef _PROFILING
   dma_end2=rtc_();
   slave_dma_count[core_id]+=((dma_end2-dma_start2));
#endif
   }else{
#ifdef _PROFILING
   dma_start3=rtc_();
#endif
     get_reply = 0;
     //core data and
     stride = (SIZEY-BLKY)*8;
     tsize = (BLKX+2)*BLKY*8;
     bsize = BLKY*8;
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj]
                   , &A_rhs[0][0]
                   , tsize, (void*)&get_reply
                   , 0, stride, bsize);
     while(get_reply !=1);
     //1.2 update left and right shade data
     get_reply=0;
     stride = (SIZEY-1)*8;
     tsize = BLKX*8;
     bsize = 8;
     athread_get(PE_MODE,&A[src][s_starti][s_startj-1]
                   , &A_ls[0]
                   , tsize,(void*)&get_reply
                   , 0, stride, bsize);
     athread_get(PE_MODE,&A[src][s_starti][s_endj]
                   , &A_rs[0]
                   , tsize,(void*)&get_reply
                   , 0, stride, bsize);  
     while(get_reply != 2);
/*     for(i=s_starti;i<s_endi;++i)
     {
        A_ls[i-s_starti]=A[src][i][s_startj-1];
        A_rs[i-s_starti]=A[src][i][s_endj];
     }*/
#ifdef _PROFILING
   dma_end3=rtc_();
   slave_dma_count[core_id]+=((dma_end3-dma_start3));
#endif
     //2.compute core data and up down line 
     for(i = 0; i < BLKX; i++)
       for(j = 1; j < BLKY-1; j++)
        {
          A_lhs[i][j] = alpha_d * (A_rhs[i+1][j]) + \
                     beta_d * (A_rhs[i][j] + A_rhs[i+2][j] +\
                     A_rhs[i+1][j-1] + A_rhs[i+1][j+1]);
        }
     //3.1compute up_left_point
     A_lhs[0][0] = alpha_d * (A_rhs[1][0]) + \
                   beta_d * (A_rhs[0][0] + A_rhs[2][0] +\
                   A_ls[0] + A_rhs[1][1]); 
     //3.2compute down_left_point
     A_lhs[BLKX-1][0] =   alpha_d * (A_rhs[BLKX][0]) +\
                     beta_d * (A_rhs[BLKX-1][0] + A_rhs[BLKX+1][0] +\
                     A_ls[BLKX-1] + A_rhs[BLKX][1]);
     //3.3compute up_right_point
     A_lhs[0][BLKY-1] = alpha_d * (A_rhs[1][BLKY-1]) + \
                     beta_d * (A_rhs[0][BLKY-1] +\
                     A_rhs[2][BLKY-1] +\
                     A_rhs[1][BLKX-2] + A_rs[0]);
     //3.4compute down_right_point
     A_lhs[BLKX-1][BLKY-1] = alpha_d * \
                     (A_rhs[BLKX][BLKY-1]) + \
                     beta_d * (A_rhs[BLKX-1][BLKY-1] + \
                     A_rhs[BLKX+1][BLKY-1] + \
                     A_rhs[BLKX][BLKY-2] + \
                     A_rs[BLKX-1]);
     //4.2 compute left and right line of i dimensional change
     for(i = 1;i < BLKX-1; ++i)
     {
        A_lhs[i][0] = alpha_d * (A_rhs[i+1][0]) + \
                     beta_d * (A_rhs[i][0] + A_rhs[i+2][0] +\
                     A_ls[i] + A_rhs[i+1][1]);
        A_lhs[i][BLKY-1] = alpha_d * (A_rhs[i+1][BLKY-1]) + \
                     beta_d * (A_rhs[i][BLKY-1] + A_rhs[i+2][BLKY-1] +\
                     A_rhs[i+1][BLKY-2] + A_rs[i]);

     }
#ifdef _PROFILING
   dma_start4=rtc_();
#endif
     //write back
     put_reply=0;
     bsize=(BLKY)*8;
     stride=(SIZEY-(BLKY))*8;  //stride: from blocktail to blockhead
     tsize= bsize*(BLKX);
     athread_put(PE_MODE,&A_lhs[0][0]
                , &A[dest][s_starti][s_startj]
                ,tsize,(void*)&put_reply,stride,bsize);
     while(put_reply!=1);
#ifdef _PROFILING
   dma_end4=rtc_();
   slave_dma_count[core_id]+=((dma_end4-dma_start4));
#endif
    last_iter = s_iter;
    last_starti = s_starti;
    last_startj = s_startj;
   }
}
